::::::::::::::::::::::::::::::::::::::::::::::::::::
Back on January 20th, 2005, forum member IntegerJim posted this intriguing message on the Wishlist board of the forum:
I would like to see a draw command such as was available in Microsoft BASIC - e.g. DRAW "BM200,200;C0U60R60D60L60" this would draw a box. Extremely complex designs are possible. There was also a REDRAW command, if memory serves me, which would act as an eraser. In short, I would like to see all the graphics capabilities that were present with Microsoft BASIC.
FYI: I am a teacher and have written tons of educational programs which I would like to convert to LBASIC.
Quickly, the learned members of the LB community realized that this "wish" could be fulfilled without a new, dedicated Liberty BASIC command. Because Liberty BASIC already has excellent string-parsing functions, it would be entirely possible to custom-build a new function which could parse the type of draw-command strings that IntegerJim identified above.
Within the day, the Emporess of the LB community, Alyce Watson, quickly scoped out a new challenge to write a DRAW() function. Alyce noted that the syntax for Microsoft's original DRAW() function could be found here, and she set February 20-21 as the days on which forum members would vote for their favorite DRAW() Challenge entry.
Unsurprisingly, this challenge was received with great enthusiasm, attracting three pages worth of followup posts! Many users wrote to identify other sources of information on the syntax and use of the DRAW() function as implemented in QBASIC. In the end, the following six forum members crafted DRAW() Challenge entries: RodBird, Janet Terra, Callum Lowcay, Anthony Webb, and Gunther. All of these marvelous entries are available here for download.
When forum voting concluded on February 21st, Anthony Webb's entry polled on top! The source code for Anthony's program is given below.
Congratulations to Anthony, and congratulations to all of the entrants and forum members who responded so enthusiastically to the challenge!
---Tom Nally, Editor
The source code for Anthony's draw.bas is give below. Note that the length of some of the code lines may extend beyond the right edge of your screen, depending on your monitor settings. If copying the code from this page is too difficult, you may find a copy of draw.bas in the zip archive that accompanies this newsletter, nl30.zip. ---Ed.
'Lb Contest entry, for Qbasic draw commands
'as issued by Alyce Watson
'Written by Anthony Webb
'All commands appear to work in QB4.5
nomainwin
dim cmdlist$(20)
gosub [fillarray]
WindowWidth=800
WindowHeight=600
UpperLeftX=10
UpperLeftY=10
menu #main,"File","New File",[newfile],|,"Open File",[openfile],|,"Save File",[savefile],"Save File As",[savefileas],|,"Exit",[quit]
menu #main,"Edit"
menu #main,"Demo","Show Demo",[showdemo]
menu #main,"Image","Save Image",[saveimage]
graphicbox #main.draw,0,0,600,400
graphicbox #main.colors,605,150,185,215
statictext #main.tex3,"Color List",670,133,100,15
statictext #main.tex1,"Command Discription",605,10,157,15
listbox #main.cmds,cmdlist$(),[showcommand],605,25,57,100
statictext #main.cmddisc," ",665,25,125,100
statictext #main.tex2,"Commands",5,405,100,20
statictext #main.tex4,"X=";lpx,200,405,40,20
statictext #main.tex5,"Y=";lpy,260,405,40,20
statictext #main.tex6,"#Cmds=";ncmds,320,405,80,20
texteditor #main.cmdtb,5,425,785,120
button #main.cmdex,"Draw",[cmdlineexecute],ul,655,375,80,20
open "Draw" for window_nf as #main
#main "trapclose [quit]"
#main.tex1 "!font arial 8 bold underscore"
#main.tex2 "!font arial 10 bold"
#main.cmds "selectindex 1"
#main.tex3 "!Font arial 10 bold"
#main.tex4 "!Font arial 10 bold"
#main.tex5 "!Font arial 10 bold"
#main.tex6 "!Font arial 10 bold"
#main.cmdtb "!font arial 12"
gosub [showcolors]
rflag=1
gosub [showdemo]
goto [showcommand]
wait
[quit]
#main.cmdtb "!contents? cmdline$"; 'get the information from the text editor
if cmdline$<>"" then
confirm "File not Saved, DO you wish to save it?"; answer$
if answer$="yes" then
rt=1
gosub [savefile]
end if
end if
close #main
end
'--------------------------------fillarray--------------------------------------
'Used to fill the array for command informtion. This is a help feature only and
'has nothing to do with the actual operation of the function
'-------------------------------------------------------------------------------
[fillarray]
cmdlist$(1)="Un"
cmdlist$(2)="Dn"
cmdlist$(3)="Ln"
cmdlist$(4)="Rn"
cmdlist$(5)="En"
cmdlist$(6)="Fn"
cmdlist$(7)="Gn"
cmdlist$(8)="Hn"
cmdlist$(9)="Mx,y"
cmdlist$(10)="B"
cmdlist$(11)="N"
cmdlist$(12)="An"
cmdlist$(13)="TAn"
cmdlist$(14)="Cn"
cmdlist$(15)="Sn"
cmdlist$(16)="Pp,b"
return
'--------------------------------showcommand------------------------------------
'used in conjunction with fillarray, displays information about the command
'selected from the commands list
'-------------------------------------------------------------------------------
[showcommand]
#main.cmds "selection? selected$"
select case selected$
case "Un"
#main.cmddisc "Move forward from the current position by n pixels"
case "Dn"
#main.cmddisc "Move opposite from the forward position by n pixels"
case "Ln"
#main.cmddisc "Move left from the current position by n pixels"
case "Rn"
#main.cmddisc "Move right from the current position by n pixels"
case "En"
#main.cmddisc "Move diagonally up and right from the current position by n pixels"
case "Fn"
#main.cmddisc "Move diagonally down and right from the current position by n pixels"
case "Gn"
#main.cmddisc "Move diagonally down and left from the current position by n pixels"
case "Hn"
#main.cmddisc "Move diagonally up and left from the current position by n pixels"
case "Mx,y"
#main.cmddisc "Move to coordinate x,y. If x is preceded by a + or -, the movement is relative to the last point referenced (LPR)"
case "B"
#main.cmddisc "A prefix command. Next movement command moves but doesn't plot"
case "N"
#main.cmddisc "A prefix command. Next movement command moves, but returns immediately to previous point"
case "An"
#main.cmddisc "Set angle. n may be 0, for 0 degrees; 1, for 90 degrees; 2, for 180 degrees; or 3, for 270 degrees. Rotated figures are rescaled to adjust to the CGA's 4/3 aspect ratio"
case "TAn"
#main.cmddisc "Turn angle. n may range from -360 degrees to +360 degrees. Positive values cause counterclockwise rotation; negative values cause clockwise rotation"
case "Cn"
#main.cmddisc "Set color to n. The default color for medium-resolution is 3; high-resolution default color is 1. See PALETTE for a list of legal colors"
case "Sn"
#main.cmddisc "Set scale factor. n may range from 1 to 255. The scaling factor used is n/4. The default for n is 4"
case "Pp,b"
#main.cmddisc "Fill figure color to paint, stopping at areas of color boundary. See PALETTE for a list of legal colors"
end select
wait
'----------------------------cmdlineexecute-------------------------------------
'used to get the information from the texteditor and then call the draw function
'-------------------------------------------------------------------------------
[cmdlineexecute]
#main.draw "cls;north;place 0 0" 'reset the drawing point on the screen to the upper left hand corner
lpx=0 'reset the last point referenced x
lpy=0 'reset the last point referenced y
#main.cmdtb "!contents? cmdline$"; 'get the information from the text editor
if len(cmdline$)>0 then 'check to make sure that there is information
a=draw(cmdline$) 'call the draw function
else
notice "No commands" 'if no information, then let the user know
end if
wait 'drawing complete
'------------------------------showcolors---------------------------------------
'Show a list of colors on the screen to help the user
'-------------------------------------------------------------------------------
[showcolors]
#main.colors "down;place 25 10;color black;backcolor black;boxfilled 75 30"
#main.colors "place 25 35;color black;backcolor darkblue;boxfilled 75 55"
#main.colors "place 25 60;color black;backcolor darkgreen;boxfilled 75 80"
#main.colors "place 25 85;color black;backcolor darkcyan;boxfilled 75 105"
#main.colors "place 25 110;color black;backcolor darkred;boxfilled 75 130"
#main.colors "place 25 135;color black;backcolor darkpink;boxfilled 75 155"
#main.colors "place 25 160;color black;backcolor brown;boxfilled 75 180"
#main.colors "place 25 185;color black;backcolor lightgray;boxfilled 75 205"
#main.colors "place 125 10;color black;backcolor darkgray;boxfilled 175 30"
#main.colors "place 125 35;color black;backcolor blue;boxfilled 175 55"
#main.colors "place 125 60;color black;backcolor green;boxfilled 175 80"
#main.colors "place 125 85;color black;backcolor cyan;boxfilled 175 105"
#main.colors "place 125 110;color black;backcolor red;boxfilled 175 130"
#main.colors "place 125 135;color black;backcolor pink;boxfilled 175 155"
#main.colors "place 125 160;color black;backcolor yellow;boxfilled 175 180"
#main.colors "place 125 185;color black;backcolor white;boxfilled 175 205"
#main.colors "place 5 25;|0="
#main.colors "place 5 50;|1="
#main.colors "place 5 75;|2="
#main.colors "place 5 100;|3="
#main.colors "place 5 125;|4="
#main.colors "place 5 150;|5="
#main.colors "place 5 175;|6="
#main.colors "place 5 200;|7="
#main.colors "place 105 25;|8="
#main.colors "place 105 50;|9="
#main.colors "place 97 75;|10="
#main.colors "place 97 100;|11="
#main.colors "place 97 125;|12="
#main.colors "place 97 150;|13="
#main.colors "place 97 175;|14="
#main.colors "place 97 200;|15="
#main.colors "flush"
return
'-------------------------------------------------------------------------------
'this is the main function for this program. First lets name and explain the
'variables used.
'scale - Used to scale the drawings. based on QB's scale command where n/4 equals
' the scale. The default is for scale is 4, meaning 4/4=1 or actual size.
'lptr - This marks the current position within the draw$ that the parser is
' looking at.
'c$ - the actual string that is passed to the draw command, that contains the
' the sequence of commands.
'goback - a flag that tells the interpeter to move back to the original starting
' point. Used with the n prefix command.
'noplot - a flag used to tell the interpeter not to draw. Used with the b prefix
' command.
'rpos - Flag used to tell the interpeter if the current move command is to be
' relative to the last refernced point or is an absolute screen position.
'ncmd - counter used to count the total number of commands.
'paint - temp variable used to hold the fill color in the paint command
'boundary - temp variable used to hold the bourder color for the paint command
'type - used to tell the "ExtFloodFill" dll command what type of fill to use.
function draw(c$)
scale=1 'set the scale to 1
lpx=0 'set the last point referenced x to upper left corner
lpy=0 'set the last point referenced y to upper left corner
#main.draw "posxy lpx lpy;down" 'place the point and set the pen down.
clen=len(c$) 'get the length of the command string
lptr=1 'set the pointer at 1 to start parsing the command string
while lptr<=clen 'start the loop, check for the end of the loop.
#main.tex4 "X=";lpx 'update the last referenced point x label
#main.tex5 "Y=";lpy 'upadte the last referenced point y label
#main.tex6 "#cmds=";ncmd 'upadte the number of commands label
a$=mid$(c$,lptr,1) 'get a character from the command string
select case a$ 'start the case statement
case ";" 'if the letter is a semicolon ignore it
'ignore this command
lptr=lptr+1 'advance the command pointer
case "S","s" 'this is the scale command
lptr=lptr+1 'advance the command pointer
gosub [getvalue] 'get the value for the scale command
if v<1 or v>255 then 'check that the value is a good value
notice "Error in scale command, ignoring"
else
scale=v/4 'set the scale
end if
ncmd=ncmd+1 'add one to the # of commands variable
case "N","n" 'This is the move but return prefix
goback=1 'set the goback flag
lptr=lptr+1 'advance the command pointer
ncmd=ncmd+1 'add one to the # of commands varianle
case "B","b" 'this is the move but dont plot prefix
noplot=1 'set the noplot flag
#main.draw "up" 'raise the pen
lptr=lptr+1 'advance the pointer
ncmd=ncmd+1 'add one to the # of commands variable
case "M","m" 'this is the move command
lptr=lptr+1 'advance the command pointer
if mid$(c$,lptr,1)="+" then 'if next command is a plus
rpos=1 'set the relative position flag
lptr=lptr+1 'advance the command pointer
end if
if mid$(c$,lptr,1)="-" then 'if the next command is a minus
rpos=2 'set the relative position flag
lptr=lptr+1 'advance the command pointer
end if
gosub [getvalue] 'get the x value of the move command
nx=v 'nx holds the temp x value
lptr=lptr+1 'advance the command pointer
gosub [getvalue] 'get the y value of the move command
ny=v 'ny holds the temp y value
if rpos=0 then 'if rpos=0 then the move is absolute
#main.draw "line ";lpx;" ";lpy;" ";nx;" ";ny 'draw a line from the last positon to the next position
lpx=nx 'set the last references point x
lpy=ny 'set the last referenced point y
#main.draw "place ";lpx;" ";lpy 'place the last referenced point for the screen
ncmd=ncmd+1 'advance the # of commands variable
else 'go to this section if relative position flag is set
if rpos=1 then 'if relative position is one then add the relative position
#main.draw "line ";lpx;" ";lpy;" ";lpx+nx;" ";lpy+ny 'draw a line from the last position to the next position
lpx=lpx+nx 'set the last referenced position x
lpy=lpy+ny 'set the last referenced position y
#main.draw "place ";lpx;" ";lpy 'place the last referenced position point for the screen
rpos=0 'reset the relative position flag
ncmd=ncmd+1 'add one to the # of commands variable
else 'if the relative position flag is greater than one then subtract the values
#main.draw "line ";lpx;" ";lpy;" ";lpx-nx;" ";lpy-ny 'draw a line from the last reference point to the new reference point
lpx=lpx-nx 'set the last referenced point x
lpy=lpy-ny 'set the last referenced point y
#main.draw "place ";lpx;" ";lpy 'place the last referenced position point for the screen
rpos=0 'reset the relative position flag
ncmd=ncmd+1 'add one to the # of commands variable
end if
end if
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "U","u" 'this is the up command
lptr=lptr+1 'advance the command pointer
gosub [getvalue] 'get the value
#main.draw "go ";v*scale 'advance in the forward position by v(value) times the scale
if goback=0 then 'check to see if the goback flag is set and if it is goback
#main.draw "posxy lpx lpy;down" 'get the last referenced point and store it in the variables
else
#main.draw "place ";lpx;" ";lpy 'set the last referenced point
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "D","d"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn 180;go ";v*scale;";turn 180"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "L","l"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn -90;go ";v*scale;";turn 90"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "R","r"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn 90;go ";v*scale;";turn -90"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "C","c"
lptr=lptr+1
gosub [getvalue]
color=v
gosub [setcolor]
ncmd=ncmd+1
case "E","e"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn 45;go ";v*scale;";turn -45"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "F","f"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn 135;go ";v*scale;";turn -135"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "G","g"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn -135;go ";v*scale;";turn 135"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
#main.draw "down" 'set the pen down incase the b prefix was used
goback=0
case "H","h"
lptr=lptr+1
gosub [getvalue]
#main.draw "turn -45;go ";v*scale;";turn 45"
if goback=0 then
#main.draw "posxy lpx lpy;down"
else
#main.draw "place ";lpx;" ";lpy
end if
ncmd=ncmd+1
goback=0
case "A","a"
lptr=lptr+1
gosub [getvalue]
select case
case 0
#main.draw "north"
case 1
#main.draw "north;turn 90"
case 2
#main.draw "north;turn 180"
case 3
#main.draw "north;turn 270"
end select
ncmd=ncmd+1
goback=0
case "T","t"
lptr=lptr+1
if upper$(mid$(c$,lptr,1))="A" then
lptr=lptr+1
gosub [getvalue]
#main.draw "north;turn ";(v*-1);
end if
ncmd=ncmd+1
goback=0
case "P","p"
#main.draw "down" 'set the pen down incase the b prefix was used
lptr=lptr+1
gosub [getvalue]
paint=v
lptr=lptr+1
gosub [getvalue]
boundary=v
hwin=hwnd(#main.draw)
type=0
'oldcolor=color
color=paint
gosub [setcolor]
#main.draw "backcolor ";clr$
color=boundary
gosub [setcolor]
CallDll #user32, "GetDC",hwin as long,hDC as long
calldll #gdi32,"ExtFloodFill" ,_
hDC as long,_
lpx as long,_
lpy as long,_
crColor as long,_
type as long,_
result as long
color=paint
gosub [setcolor]
#main.draw "color ";clr$
ncmd=ncmd+1
goback=0
case else
lptr=lptr+1
end select
wend
goto [end]
[setcolor]
select case color
case 0
clr$="black"
crColor=0
case 1
clr$="darkblue"
crColor=8388608
case 2
clr$="darkgreen"
crColor=32768
case 3
clr$="darkcyan"
crColor=8421376
case 4
clr$="darkred"
crColor=128
case 5
clr$="darkpink"
crColor=8388736
case 6
clr$="brown"
crColor=32896
case 7
clr$="lightgray"
crColor=12632256
case 8
clr$="darkgray"
crColor=8421504
case 9
clr$="blue"
crColor=16711680
case 10
clr$="green"
crColor=65280
case 11
clr$="cyan"
crColor=16776960
case 12
clr$="255 0 0"
crColor=255
case 13
clr$="pink"
crColor=16711935
case 14
clr$="yellow"
crColor=65535
case 15
clr$="white"
crColor=16777215
end select
#main.draw "color ";clr$
return
[getvalue]
v$=""
while instr("0123456789-+. ",mid$(c$,lptr,1))>0
v$=v$+mid$(c$,lptr,1)
lptr=lptr+1
wend
v=val(v$)
return
[end]
#main.draw "getbmp temp 0 0 600 400"
#main.draw "drawbmp temp 0 0"
#main.draw "flush"
unloadbmp "temp"
end function
[openfile]
filedialog "Open Draw File","*.txt",f$
if f$="" then wait
open f$ for input as #1
#main.cmdtb "!contents #1"
close #1
wait
#main.cmdtb dcmd$
#main.draw "cls;north"
lpx=0
lpy=0
a=draw(dcmd$)
wait
[savefile]
#main.cmdtb "!contents? textstr$"
if f$<>"" then
open f$ for output as #1
print #1,textstr$
close #1
if rt=1 then return
else
goto [savefileas]
end if
wait
[savefileas]
filedialog "Save File As","*.txt",f$
if f$="" then wait
open f$ for output as #1
print #1,textstr$
close #1
if rt=1 then return
wait
[newfile]
#main.cmdtb "!cls"
lpx=0
lpy=0
#main.draw "cls"
#main.draw "north"
#main.draw "place 0 0"
wait
[showdemo]
#main.draw "cls;north;place 0 0"
lpx=0
lpy=0
d$="c8;s4;bm300,200;u20;l5;u4;r5;u10;h20;r40;g20;d10;r5;d4;l5;d20;l12;be5;p7,8;c8;bu40;bl12;r5;br3;r5;br3;r5;br3;r5;u1;l6;bl3;l5;bl3;l5;bl3;l5;bd12;br12;d32;br3;bu1;u32;br3;bd1;d31;c14;bu47;bl13;s6;u3;h6;u3;h3;u5;e4;u2;e10;r3;e5;f4;d3;f3;d3;f6;d7;g4;d3;g7;l13;be5;bu5;p14,14;c4;h1;u1;h1;l2;u7;e4;r1;e7;f4;d8;g3;d1;l3;d1;g3;d2;h2;u1;be5;bu2;p12,4"
d$=d$+"c0;bl60;bu15;l10;d65;r40;u10;l30;u55;bg5;p9,0;c0;be5;l10;e10;r10;g10;bu5;p1,0;c0;bd5;e10;d55;g10;e5;bu5;p1,0;c0;bd5;e5;r30;g10;bu5;p1,0;c0;bd5;e10;d10;g10;e5;bu5;p1,0;c0;bd5;bg4;br60"
d$=d$+"c0;u65;r30;f10;d18;g10;f10;d17;g10;l30;be10;u18;r15;f10;d4;g10;l15;bu30;u18;r15;f10;d4;g10;l15;bg5;p14,0"
d$=d$+"c0;bu32;bl4;e10;r30;g10;u1;bh5;p6,0;c0;bu3;br11;f10;g10;bu5;p6,0;c0;bd5;e10;d18;g10;e5;bu5;p6,0;c0;bd5;g15;e10;f10;g10;e5;bu5;p6,0;c0;bd5;be6;d17;g10;e5;bu5;p6,0"
d$=d$+"c0;bl33;bd9;e10;nu12;nr14;c12;d5;c0;p6,0;c0;bu35;ng11;nu11;nr14;bd5;p6,0"
a=draw(d$)
if rflag=1 then
rflag=0
return
end if
wait
[saveimage]
filedialog "Save Image as","*.bmp",bf$
if bf$="" then wait
#main.draw "getbmp temp 0 0 600 400"
bmpsave "temp",bf$
unloadbmp "temp"
notice "Image saved as ";bf$
wait